package com.heima.wemedia.service.impl;

import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.Dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.buider.ArticleDtoBuilder;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.itheima.apis.article.IArticleClient;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional//开启事务
@Slf4j
public class WmNewsAutoScanImpl implements WmNewsAutoScanService {

    //注入文章
    @Autowired
    private WmNewsMapper wmNewsMapper;

    //注入阿里元文本审核
    @Autowired
    private GreenTextScan greenTextScan;

    //注入阿里云图片审核
    @Autowired
    private GreenImageScan greenImageScan;

    //注入Minio下载工具类
    @Autowired
    private FileStorageService fileStorageService;

    //注入用户服务
    @Autowired
    private WmUserMapper wmUserMapper;

    //注入feign接口服务调用保存方法
    @Autowired
    private IArticleClient articleClient;

    //注入频道服务
    @Autowired
    private WmChannelMapper wmChannelMapper;

    //注入敏感词服务
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;

    //注入tess4j的图片审核服务
    @Autowired
    private Tess4jClient tess4jClient;



    //自媒体文章自动审核
    //文章id
    @Override
    @Async//标明当前方法是异步调用
    //@GlobalTransactional//分布式事务管理
    public void autoScanWmNews(Integer id) throws Exception {
        //1.根据文章的id查询文章
        WmNews wmNews = wmNewsMapper.selectById(id);

        //如果没有查询到文章则抛出异常
        if (null == wmNews) {
            throw  new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            //解析文章的内容数据--所有文本内容和所有的图片内容
            Map<String, Object> textAndImages = handleTextAndImages(wmNews);

            //在上传阿里云审核之前 先将文本内容在自定义的敏感词中审核一下,没通过就不用在上传了
            Boolean isSensitive = handleSensitiveScan((String) textAndImages.get("text"),wmNews);
            if (!isSensitive){
                return;
            }

            //把文本内容上传到阿里云进行审核--审核失败,审核文化在那个的状态:2 -人工审核更新为3
            Boolean status = handleTextScan(textAndImages.get("text").toString(),wmNews);
            //判断如果审核状态为false就不用继续了
            if (!status) {
                return;
            }

            //把所有的图片上传下载
            //把所有图片的二进制文件上传到阿里云进行图片审核 -审核文章的状态为:2 -人工审核更新为:3
            status = handleImageScan((List<String>)textAndImages.get("images"),wmNews);
            if (!status) {
                return;
            }

            //同步文章数据到文章微服务
            ResponseResult responseResult = saveAppArticle(wmNews);
            if (responseResult.getCode() != 200){
                throw new CustomException(AppHttpCodeEnum.DATA_SYNC_FAIL);
            }

            //设个异常验证事务
            //int sb =1/0;

            //更新文章的审核状态为:9
            updateWmNews(wmNews, WmNews.Status.PUBLISHED, "审核通过");
        }

    }

    //自管理的敏感词审核判断
    private boolean handleSensitiveScan(String text, WmNews wmNews) {

        Boolean flag = Boolean.TRUE;

        //获取所有的敏感词将获取到的对象转换为字符串
        List<WmSensitive> wmSensitives = wmSensitiveMapper
                .selectList(Wrappers.<WmSensitive>lambdaQuery()
                .select(WmSensitive::getSensitives));//投影查询-不查完整的列,只查指定列
        //转换为字符串类型
        List<String> sensitives = wmSensitives.stream().map(WmSensitive::getSensitives)
                .collect(Collectors.toList());

        //初始化DFA算法敏感词库
        SensitiveWordUtil.initMap(sensitives);

        //判断查看文章中是否包含敏感词并反馈失败提示信息
        Map<String, Integer> map = SensitiveWordUtil.matchWords(text);

        if (null != map && map.size() > 0) {
            //看看更新方法要传什么参数
            updateWmNews(wmNews,WmNews.Status.FAIL,"自定义敏感词审核失败了 = " + map);
            //失败就返回false结果
            flag = Boolean.FALSE;
        }

        //返回判断结果
        return flag;
    }

    //更新文章方法
    private void updateWmNews(WmNews wmNews, WmNews.Status published, String reson) {
        wmNews.setStatus(published.getCode());
        wmNews.setReason(reson);
        //更新文章
        wmNewsMapper.updateById(wmNews);
    }


    //数据从自媒体端同步到文章微服务-并将一些不同的属性做处理
    private ResponseResult saveAppArticle(WmNews wmNews) throws Exception {

//        //文章保存数据需要dto类型 所以需要将wmNews数据拷贝到dto
//        ArticleDto dto = new ArticleDto();
//        BeanUtils.copyProperties(wmNews,dto);
//
//        //并且对一些不能正常拷贝的数据做处理封装
//        //单独处理作者相关的数据,例行非空
//        if (wmNews.getUserId() != null) {
//            //需要查询作者数据然后赋值给文章保存dto
//            WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
//            dto.setAuthorId(wmNews.getUserId());
//            dto.setAuthorName(wmUser.getName());
//        }
//        //单独处理频道相关的数据
//        //非空判断-然后
//        if (wmNews.getChannelId() != null) {
//            WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
//            dto.setChannelName(wmChannel.getName());
//        }
//        //单独处理其他相关的数据
//        //文章的布局
//        dto.setLayout(wmNews.getType());
//
//
//        //因为自媒体端的文章保存涉及远程调用文章服务article 所以要注入暴露的feign接口来调用服务保存
//        ResponseResult responseResult = articleClient.saveArticle(dto);
//
//
//        //返回结果
//        return responseResult;
//        ArticleDtoBuilder articleDtoBuilder = new ArticleDtoBuilder(wmNews, wmUserMapper, wmChannelMapper);
//        //使用构造器设计模式 方便以后维护
//        //构造器设计模式：他最适合用于构建复杂得对象
//        // 1、VO 对象
//        // 2、一个系统调用另一个系统得时候， 另一个系统，需要一个对象来操作,而且这个对象本身还挺复杂\
//        ArticleDto articleDto = articleDtoBuilder.buildBeanCopy()
//                .buildAuthor()
//                .buildBasic()
//                .buildChannel()
//                .builder();
//        return articleClient.saveArticle(articleDto);
        ArticleDto dto = new ArticleDtoBuilder(wmNews, wmUserMapper, wmChannelMapper)
                .buildBeanCopy()
                .buildAuthor()
                .buildChannel()
                .basicCopy()
                .bulider();
        ResponseResult responseResult = articleClient.saveArticle(dto);

        return responseResult;
    }


    //图片审核判断
    private Boolean handleImageScan(List<String> images, WmNews wmNews) throws Exception {
        //同样创建一个布尔类型变量flag
        Boolean flag = Boolean.TRUE;

        //例行非空判断
        if(null == images || images.size() == 0){
            return flag;
        }

        //对图片去重
        images = images.stream().distinct().collect(Collectors.toList());

        //创建一个集合装图片地址数据 注意下载下来的图片都是是字节数组 因为阿里云要上传的就字节形式的数据
        List<byte[]> imageUrls = new ArrayList<>();
            //下载每一个图片
            for (String url : images) {
                byte[] bytes = fileStorageService.downLoadFile(url);
                imageUrls.add(bytes);

                //在这里调tess4j审核图片
                //ba获取到的bytes字节转换为butteredImage
                ByteArrayInputStream is = new ByteArrayInputStream(bytes);
                //然后用图片的IO流读取转为图片
                BufferedImage image = ImageIO.read(is);
                //进行文字识别获取
                String str = tess4jClient.doOCR(image);

                System.err.println(str);

                //最后将获取到的文字 传入自管理的审核方法中判断后将获取的结果返回
                Boolean status = handleSensitiveScan(str, wmNews);
                if (!status) {
                    flag = Boolean.FALSE;
                    //返回结果
                    return flag;
                }
            }

        if (null == imageUrls || imageUrls.size() == 0) {
            return flag;
        }
            //然后上传到阿里云审核
            Map map = greenImageScan.imageScan(imageUrls);


            //然后根据阿里云返回的审核关键字判断是否通过并返回对应的提示信息
            //如果返回值等于black表示审核失败--审核文章的状态为:2
            if ("block".equals(map.get("suggestion"))) {
                updateWmNews(wmNews, WmNews.Status.FAIL, "文章审核失败,原因为:内容包含违禁词");
                flag = Boolean.FALSE;

                //如果返回值等于review 就提示需要人工审核状态为:3 去枚举里面看
            } else if ("review".equals(map.get("suggestion"))) {
                updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, "文章已转接人工审核,原因为:内容包含模糊信息");
                flag = Boolean.FALSE;
            }

            //走到这里就表示文章审核通过了
            return flag;

    }


    //文本审核
    private Boolean handleTextScan(String text, WmNews wmNews) throws Exception {

        //定义一个布尔类型的flag
        Boolean flag = Boolean.TRUE;

        //例行判断非空
        if (StringUtils.isEmpty(text)) {
            return flag;
        }

        //上传文本
        Map map = greenTextScan.greeTextScan(text);

        //然后根据阿里云返回的审核关键字判断是否通过并返回对应的提示信息
        //如果返回值等于black表示审核失败--审核文章的状态为:2
        if ("block".equals(map.get("suggestion"))) {
            updateWmNews(wmNews, WmNews.Status.FAIL, "文章审核失败,原因为:内容包含违禁词");

            flag = Boolean.FALSE;
        }
        //如果返回值等于review 就提示需要人工审核状态为:3 去枚举里面看
        if ("review".equals(map.get("suggestion"))) {
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, "文章已转接人工审核,原因为:内容包含模糊信息");
            flag = Boolean.FALSE;
        }
        //走到这里就表示文章审核通过了
        return flag;

    }


    //抽取文章中的文本和图片内容
    private Map<String,Object> handleTextAndImages(WmNews wmNews) {

        //创建两个对象装抽取出来的文本和图片数据
        StringBuilder sb = new StringBuilder();
        List<String> imageUrls = new ArrayList<>();

        //解析内容中的文本和图片,非空判断 JSON数据转换为Map类型
        if (StringUtils.isNotEmpty(wmNews.getContent())) {
            List<Map> mapList = JSON.parseArray(wmNews.getContent(), Map.class);
            //遍历获取字段名为image的图片和文本通过k-v判断
            for (Map map : mapList) {
                if ("image".equals(map.get("type"))) {
                    imageUrls.add(map.get("value").toString());
                }else {
                    sb.append(map.get("value").toString());
                }
            }
        }

        //处理封面的图片,非空判断
        if (StringUtils.isNotEmpty(wmNews.getImages())) {
            String[] images = wmNews.getImages().split(",");
            imageUrls.addAll(Arrays.asList(images));
        }

        //处理标题的文本
        sb.append(wmNews.getTitle());

        //将结果返回map类型
        Map<String , Object> map = new HashMap<>();
        map.put("text",sb.toString());
        map.put("images",imageUrls);

        return map;

    }
}
